﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.IO;
using System.Globalization;
using Microsoft.Search.Interop;
using System.Diagnostics;
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
using System.Data.OleDb;
using ClipShow2.Storage;
using ClipShow2.MediaTypes;

namespace ClipShow2.MediaFinder
{
    public class SearchEngine
    {
        public SearchEngine()
        {
        }
        
        private const string WDS_SEARCH_COLUMNS = "\"System.ItemUrl\",\"System.Media.Duration\",\"System.ItemDate\",\"System.Title\",\"System.Comment\",\"System.Author\",\"System.Media.SubTitle\",\"System.ParentalRating\"";
        private static OleDbConnection _connection;
        private static OleDbDataReader ExecuteReader(string query)
        {
            string connectionString = "Provider=Search.CollatorDSO;Extended Properties='Application=Windows';";
            _connection = new OleDbConnection(connectionString);
            _connection.Open();
            OleDbCommand command = new OleDbCommand();
            command.Connection = _connection;
            command.CommandText = query;
            OleDbDataReader reader = command.ExecuteReader();
            return reader;
        }
        private static string WHSMediaExtensions()
        {
            //ext:(.mp4 OR .m4a OR .3gp OR .3g2 OR .aac OR .avi OR .m2t OR .m2ts OR .mts OR .wmv OR .mov)
            string RetVal = "";
  
            foreach (string t in StorageEngine.GetSettings().WHSExtensions)
            {
                RetVal = RetVal + String.Format("{0} OR ", t);
            }

            return (String.Format("System.FileExtension:({0})",RetVal.Remove(RetVal.Length-4)));
        }
        private static ClipShowMetadata ExtractFromWDS(string sourceFilePath, OleDbDataReader reader)
        {
            ClipShowMetadata mt = new ClipShowMetadata();
            mt.Url = sourceFilePath;

            try
            {
                Decimal duration = (Decimal)reader.GetValue(1);
                mt.Duration = Convert.ToInt64(duration) / 10000;
            }
            catch
            {
                mt.Duration = -1;
            }
            mt.CreationDateTime = (DateTime)reader.GetValue(2);

            try
            {
                mt.Title = (string)reader.GetValue(3);
            }
            catch
            {
                mt.Title = Path.GetFileNameWithoutExtension(sourceFilePath);
            }

            try
            {
                mt.Description = (string)reader.GetValue(4);
            }
            catch
            {
                mt.Description = "";
            }

            try
            {
                mt.Author = (string)reader.GetValue(5);
            }
            catch
            {
                mt.Author = "";
            }

            try
            {
                mt.ParentalRating = (string)reader.GetValue(7);
            }
            catch
            {
                mt.ParentalRating = "unrated content";
            }

            string[] temp = mt.Url.Split('\\');

            string hashedFile = "";
            string targetFile = "";
            if ((temp[0].Equals("") && (temp[1].Equals(""))))
            {
                // We have a remote machine
                string targetDir = String.Format("{0}\\{1}",
                    StorageEngine.GetThumbnailDir(), temp[2]);
                if (!Directory.Exists(targetDir))
                {
                    Directory.CreateDirectory(targetDir);
                }
                hashedFile = Path.GetDirectoryName(mt.Url).Replace(@"\", "_");
                targetFile = String.Format("{0}\\{1}_{2}.jpg",
                    targetDir,
                    hashedFile.Replace(":", "_"),
                    Path.GetFileNameWithoutExtension(mt.Url));

            }
            else
            {
                hashedFile = Path.GetDirectoryName(mt.Url).Replace(@"\", "_");
                targetFile = String.Format("{0}\\{1}_{2}.jpg",
                StorageEngine.GetThumbnailDir(),
                hashedFile.Replace(":", "_"),
                Path.GetFileNameWithoutExtension(mt.Url));
            }

            mt.ThumbnailUrl = targetFile;
            mt.IsTempThumbnail = (!File.Exists(targetFile));
            return (mt);
        }
        
        /// <summary>
        /// Create groupings based upon creation date criteria
        /// </summary>
        /// <param name="source">Source clip show list</param>
        /// <returns>Group clip show list</returns>
        private static ClipShowContent FindGroupings(ClipShowContent source)
        {
            // Sort media by timeline
            MediaByDateComparer mdc = new MediaByDateComparer();
            source.MediaItems.Sort(mdc);

            Dictionary<string, ClipShowMetadata> groupedItems = new Dictionary<string, ClipShowMetadata>();
            foreach (ClipShowMetadata item in source.MediaItems)
            {
                if (groupedItems.ContainsKey(item.CreationDateTime.ToShortDateString()))
                {
                    ClipShowMetadata keyItem = groupedItems[item.CreationDateTime.ToShortDateString()];
                    keyItem.IsGroupItem = true;
                    keyItem.GroupItemDescription = item.CreationDateTime.ToShortDateString();
                    if (!item.Duration.Equals(-1))
                    {
                        keyItem.GroupDuration += item.Duration;
                    }

                    if (keyItem.GroupItems.Count.Equals(0))
                    {
                        ClipShowMetadata temp = new ClipShowMetadata();
                        temp.ThumbnailUrl = keyItem.ThumbnailUrl;
                        temp.Title = keyItem.Title;
                        temp.Url = keyItem.Url;
                        temp.CreationDateTime = keyItem.CreationDateTime;
                        temp.Description = keyItem.Description;
                        temp.Duration = keyItem.Duration;
                        temp.Author = keyItem.Author;
                        temp.ParentalRating = keyItem.ParentalRating;
                        keyItem.GroupItems.Add(temp);
                    }

                    keyItem.GroupItems.Add(item);
                }
                else
                {
                    groupedItems.Add(item.CreationDateTime.ToShortDateString(), item);
                }
            }

            ClipShowContent result = new ClipShowContent();
            result.ContentSourceType = source.ContentSourceType;
            result.ContentDuration = source.ContentDuration;
            foreach (ClipShowMetadata item in groupedItems.Values)
            {
                if (item.IsGroupItem)
                {
                    item.Description = String.Format("{0}\n({1} clips grouped by date)",
                        item.Description, item.GroupItems.Count);
                }
                result.MediaItems.Add(item);
            }

            return (result);
        }

        //Revised search code post build 1211
        public static ClipShowContent QueryWindowsSearchLocal(string QueryString)
        {
            ClipShowContent rootCm = new ClipShowContent();
            Settings mySettings = StorageEngine.GetSettings();
            try
            {
                // Use Windows Search to find media in local index
                CSearchManager manager = new CSearchManagerClass();
                CSearchCatalogManager catalogManager = manager.GetCatalog("SystemIndex");
                CSearchQueryHelper queryHelper = catalogManager.GetQueryHelper();
                rootCm.ContentSourceType = ContentSource.Local;
                queryHelper.QuerySelectColumns = WDS_SEARCH_COLUMNS;
                string query;

                if (QueryString.Equals(""))
                {
                    query = queryHelper.GenerateSQLFromUserQuery("* System.Kind:videos System.Search.Store:file");
                }
                else
                {
                    query = queryHelper.GenerateSQLFromUserQuery(String.Format("{0} System.Kind:videos System.Search.Store:file", QueryString));
                }

                OleDbDataReader reader = ExecuteReader(query);
                while (reader.Read())
                {
                    Uri convert = new Uri(reader.GetString(0));
                    if (!Path.GetExtension(convert.LocalPath).Equals(".asx"))
                    {
                        if (File.Exists(convert.LocalPath))
                        {
                            ClipShowMetadata item = ExtractFromWDS(convert.LocalPath, reader);
                            rootCm.MediaItems.Add(item);
                            if (!item.Duration.Equals(-1))
                            {
                                rootCm.ContentDuration += TimeSpan.FromMilliseconds(item.Duration);
                            }
                        }
                    }
                }
                reader.Close();
            }
            catch (SystemException se)
            {
                Debug.WriteLine(se.Message);
            }

            if (mySettings.GroupAlikeClips)
            {
                // Find groupings and categorize those
                return (FindGroupings(rootCm));
            }
            else
            {
                return (rootCm);
            }
        }
        public static ClipShowContent QueryWindowsSearchWHS(string QueryString)
        {
            ClipShowContent rootCm = new ClipShowContent();
            Settings mySettings = StorageEngine.GetSettings();
            try
            {
                CSearchManager manager = new CSearchManagerClass();
                CSearchCatalogManager catalogManager = manager.GetCatalog("SystemIndex");
                rootCm.ContentSourceType = ContentSource.WindowsHomeServer;

                #region Windows Home Server search
                // Now check for an existing WHS
                try
                {
                    // WHS 2003 Index does not recognize the same video file formats that Win7 does
                    // So we will use a seperate query string that searchs for file types (via extensions) in the Video folder
                    CSearchQueryHelper queryWHSHelper = catalogManager.GetQueryHelper();
                    queryWHSHelper.QuerySelectColumns = WDS_SEARCH_COLUMNS;

                    string whsQuery = "";
                    if (QueryString.Equals(""))
                    {
                        whsQuery = queryWHSHelper.GenerateSQLFromUserQuery(
                            String.Format("* {0} System.Search.Store:file", WHSMediaExtensions()));
                    }
                    else
                    {
                        whsQuery = queryWHSHelper.GenerateSQLFromUserQuery(String.Format("{0} {1} System.Search.Store:file", 
                            QueryString,WHSMediaExtensions()));
                    }

                    string whsName = mySettings.WHSName;
                    string whsVideoPath = mySettings.WHSVideoPath;

                    // Modify query for Windows Home Server
                    string remoteQuery = String.Format("SELECT {0} FROM {1}.SystemIndex {2} AND SCOPE='file:{3}'",
                        WDS_SEARCH_COLUMNS,
                        whsName,
                        whsQuery.Substring(whsQuery.IndexOf("WHERE ")),
                        whsVideoPath.Replace(@"\", "/"));

                    OleDbDataReader remoteReader = ExecuteReader(remoteQuery);
                    while (remoteReader.Read())
                    {
                        Uri convert = new Uri(remoteReader.GetString(0));
                        if (!Path.GetExtension(convert.LocalPath).Equals(".asx"))
                        {
                            if (File.Exists(convert.LocalPath))
                            {
                                ClipShowMetadata item = ExtractFromWDS(convert.LocalPath, remoteReader);
                                if (!item.Duration.Equals(-1))
                                {
                                    rootCm.ContentDuration += TimeSpan.FromMilliseconds(item.Duration);
                                }
                                rootCm.MediaItems.Add(item);
                            }
                        }
                    }
                    remoteReader.Close();
                }
                catch (SystemException se)
                {
                    Debug.WriteLine(se.Message);
                }
                #endregion
            }
            catch (SystemException se)
            {
                Debug.WriteLine(se.Message);
            }

            if (mySettings.GroupAlikeClips)
            {
                // Find groupings and categorize those
                return (FindGroupings(rootCm));
            }
            else
            {
                return (rootCm);
            }
  
        }
        public static ClipShowContent QueryWindowsSearchHG(string QueryString)
        {
            ClipShowContent rootCm = new ClipShowContent();
            Settings mySettings = StorageEngine.GetSettings();

            try
            {
                // Use Windows Search to find media in local index
                CSearchManager manager = new CSearchManagerClass();
                CSearchCatalogManager catalogManager = manager.GetCatalog("SystemIndex");
                CSearchQueryHelper queryHelper = catalogManager.GetQueryHelper();
                rootCm.ContentSourceType = ContentSource.Homegroup;
                queryHelper.QuerySelectColumns = WDS_SEARCH_COLUMNS;
                string query = "";
                
                if (QueryString.Equals(""))
                {
                    query = queryHelper.GenerateSQLFromUserQuery("* System.Kind:videos System.Search.Store:file");
                }
                else
                {
                    query = queryHelper.GenerateSQLFromUserQuery(String.Format("{0} System.Kind:videos System.Search.Store:file", QueryString));
                }

                #region Homegroup search
                NonFileSystemKnownFolder homeGroup = (NonFileSystemKnownFolder)ShellLibrary.FromParsingName("::{B4FB3F98-C1EA-428D-A78A-D1F5659CBA93}");

                foreach (ShellObject so in homeGroup)
                {
                    ShellProperties sp = so.Properties;
                    string prop = (string)sp.GetProperty("System.KindText").ValueAsObject;
                    if (prop.Equals("Folder"))
                    {
                        ShellContainer sc = so as ShellContainer;

                        string itemName = (string)sp.GetProperty("System.ItemName").ValueAsObject;
                        int x = itemName.IndexOf('(');
                        // Grab remote machine name
                        string serverName = itemName.Substring(x + 1, itemName.Length - x - 2).Trim();

                        foreach (ShellObject child in sc)
                        {
                            ShellProperties childProperties = child.Properties;
                            string myType = (string)childProperties.GetProperty("System.FileName").ValueAsObject;
                            // Find the Videos Homegroup folder
                            if (myType.Equals("Videos"))
                            {
                                ShellContainer videos = child as ShellContainer;
                                foreach (ShellObject videoItem in videos)
                                {
                                    ShellProperties vProp = videoItem.Properties;
                                    string path = (string)vProp.GetProperty("System.ParsingPath").ValueAsObject;

                                    string remoteHGQuery = String.Format("SELECT {0} FROM {1}.SystemIndex {2} AND SCOPE='file:{3}'",
                                        WDS_SEARCH_COLUMNS,
                                        serverName,
                                        query.Substring(query.IndexOf("WHERE ")),
                                        path.Replace(@"\", "/"));

                                    try
                                    {
                                        OleDbDataReader remoteHGReader = ExecuteReader(remoteHGQuery);
                                        while (remoteHGReader.Read())
                                        {
                                            Uri convert = new Uri(remoteHGReader.GetString(0));
                                            if (!Path.GetExtension(convert.LocalPath).Equals(".asx"))
                                            {
                                                if (File.Exists(convert.LocalPath))
                                                {
                                                    ClipShowMetadata item = ExtractFromWDS(convert.LocalPath, remoteHGReader);
                                                    if (!item.Duration.Equals(-1))
                                                    {
                                                        rootCm.ContentDuration += TimeSpan.FromMilliseconds(item.Duration);
                                                    }
                                                    rootCm.MediaItems.Add(item);
                                                }
                                            }
                                        }
                                        remoteHGReader.Close();
                                    }
                                    catch { }//Ignore exceptions from homegroup search

                                }
                                break;
                            }

                        }
                    }
                }
                #endregion
            }
            catch (SystemException se)
            {
                Console.WriteLine(se.Message);
            }

            if (mySettings.GroupAlikeClips)
            {
                // Find groupings and categorize those
                return (FindGroupings(rootCm));
            }
            else
            {
                return (rootCm);
            }

        }
        public static ClipShowContent QueryWindowsSearchAll(string QueryString)
        {
            ClipShowContent results = SearchEngine.QueryWindowsSearchLocal(QueryString);
            Settings mySettings = StorageEngine.GetSettings();
            results.ContentSourceType = ContentSource.SearchResults;

            // Check for WHS configuration on local machine
            if (mySettings.WHSAvailable)
            {
                if (mySettings.IncludeWindowsHomeServer)
                {
                    try
                    {
                        ClipShowContent cscWHS = SearchEngine.QueryWindowsSearchWHS(QueryString);
                        if (cscWHS.MediaItems.Count > 0)
                        {
                            results.MediaItems.AddRange(cscWHS.MediaItems);
                            results.ContentDuration += cscWHS.ContentDuration;
                        }
                    }
                    catch
                    {
                        // No notification
                    }
                }

            }

            // Check HomeGroup
            if (mySettings.IncludeHomegroup)
            {
                try
                {
                    ClipShowContent cscHG = SearchEngine.QueryWindowsSearchHG(QueryString);
                    if (cscHG.MediaItems.Count > 0)
                    {
                        results.MediaItems.AddRange(cscHG.MediaItems);
                        results.ContentDuration += cscHG.ContentDuration;
                    }

                }
                catch
                {
                    // No notification
                }
            }

            return (results);
        }
    }
}
